# -*- coding: utf-8 -*-
from socket import *
import serial
import prettytable
import base64

import yaml
import threading
import time

from queue import Queue
import datetime

now = datetime.datetime.now()
config_path = "config.yml"
yml = yaml.safe_load(open(config_path))


class GGAThread(threading.Thread):
    def __init__(self, dev: str, baud_rate: int, q: Queue, rtcmq: Queue, time_out=10):
        super().__init__()
        self.dev = dev
        self.baud_rate = baud_rate
        self.time_out = time_out
        self.gga_que = q
        self.rtcm_que = rtcmq
        self.output_to_gga = yml["output_to_gga"]
        self.serial = serial.Serial(self.dev, self.baud_rate, timeout=self.time_out)
        filename = yml['save_path'] + "_%s" % (now.strftime("%Y%m%d_%H%M%S_.gnss"))
        self.gga_file = open(filename,'wb')
        if not self.serial.isOpen():
            self.serial.open()
            print("Open ",self.dev)
        if not self.serial.isOpen():
            raise PermissionError("{} open faield\n".format(self.dev))

    def __get_gga(self):
        """
        读取buffer里面所有数据x
        :return:
        """
        while True:
            msg = ''
            try:
                while self.serial.inWaiting() <= 0:
                    time.sleep(0.5)
                c = self.serial.readall()
                self.gga_file.write(c)
                msg += c.decode('utf-8')
            except UnicodeDecodeError as e:
                print(str(e))
            except OSError as e:
                print(str(e))
            index = max(msg.find("$GPGGA"), msg.find("$GNGGA"))
            if index < 0:
                continue
            msg = msg[index:-1]
            print("GGA GET:",msg)
            return msg

    def run(self) -> None:
        while True:
            if not self.serial.isOpen():
                self.serial.open()
            msg = self.__get_gga()
            self.gga_que.put(msg)
            if self.output_to_gga:
                rtcm = self.rtcm_que.get(block=False)
                if rtcm is not None:
                    self.serial.write(rtcm)


def isGnggaValid(msg: str):
    """
    检查GGA有效性
    :param msg:
    :return:
    """
    if msg is None:
        return False
    if msg == "":
        return False
    if not (msg.startswith("$GNGGA") or msg.startswith("$GPGGA")):
        return False
    msgs = msg.split(',')
    return msgs[6] == '1' or msgs[6] == '4' or msgs[6] == '5' or msgs[6] == '2'


def show_source_table(recv_data):
    recv_data = recv_data.strip()
    recv_data = recv_data.split("\n")
    t = prettytable.PrettyTable(header=False)
    for r in recv_data[1:]:
        if not r.startswith('STR'): continue
        rs = r.strip().split(";")
        t.add_row(rs)
    print(t)


def request_source_table(url, ntrip_port, ntrip_client_agent):
    tcp_client_socket = socket()
    tcp_client_socket.connect((url, ntrip_port))
    req = "GET / HTTP/1.0\r\nUser-Agent: %s\r\nAccept: */*\r\nConnection: close\r\n\r\n" % ntrip_client_agent
    tcp_client_socket.send(req.encode())
    recv_data = tcp_client_socket.recv(1024)  #
    tcp_client_socket.close()
    return recv_data.decode('utf-8')


def request_for_RTCM3():
    ntrip_user = yml.get("ntrip_user")
    ntrip_passowrd = yml.get("ntrip_passowrd")
    ntrip_client_agent = yml.get("ntrip_client_agent")
    ntrip_server = yml.get("ntrip_server")
    mount_point = yml.get("mount_point")
    ntrip_port = yml.get("ntrip_port")
    serial_port_gga_in = yml.get("serial_port_gga_in")
    baud_rate = yml.get("baud_rate")
    time_out = yml.get("time_out")
    serial_ports_rtcm_out = yml.get("serial_ports_rtcm_out")
    save_path = yml.get("save_path")

    save_path = save_path + "_%s" % (now.strftime("%Y%m%d_%H%M%S_.rtcm3"))

    rtcm_file = open(save_path, 'wb')
    if not rtcm_file:
        rtcm_file.close()

    res = request_source_table(ntrip_server, ntrip_port, ntrip_client_agent)
    show_source_table(res)

    userinfo = base64.encodebytes(("%s:%s" % (ntrip_user, ntrip_passowrd)).encode()).decode('utf-8')
    req = "GET /%s HTTP/1.0\r\nUser-Agent: %s\r\nAccept: */*\r\nConnection: close\r\nAuthorization: Basic %s\r\n\r\n" % (
        mount_point, ntrip_client_agent, userinfo)
    tcp_client_socket = socket()
    tcp_client_socket.connect((ntrip_server, ntrip_port))
    tcp_client_socket.send(req.encode())
    recv_data = tcp_client_socket.recv(1024).decode('utf-8')  #
    if not recv_data.startswith("ICY 200 OK"):
        print(" Authorization Failed!:%s" % recv_data)
        return
    print("Authorization Success")
    outports = []
    if len(serial_ports_rtcm_out) > 0:
        for dev in serial_ports_rtcm_out:
            if dev != serial_port_gga_in:
                print("start output to %s" % dev)
                outports.append(serial.Serial(dev, baud_rate, timeout=time_out))
    receive_size = 0
    print("Open serial port: %s" % serial_port_gga_in)
    que = Queue()
    rtcm_q = Queue()
    print("start GGA thread")
    t = GGAThread(serial_port_gga_in, baud_rate, que, rtcm_q, time_out)
    t.start()
    msg = que.get(block=True)
    if msg is not None:
        tcp_client_socket.send(msg.decode())
    write_to_gga_port = yml["output_to_gga"]
    while True:
        recv_data = tcp_client_socket.recv(1024)  #
        # s.write(recv_data)
        if rtcm_file:
            rtcm_file.write(recv_data)
            rtcm_file.flush()
        for dev in outports:
            length = dev.write(recv_data)
            if length < 0:
                print("Serial Port closed%s" % (dev.name))
                dev.close()
                outports.remove(dev)
        receive_size = receive_size + len(recv_data)
        if write_to_gga_port:
            rtcm_q.put(recv_data)
        ggamsg = que.get(block=False, timeout=0)
        if ggamsg is not None:
            if isGnggaValid(msg):
                tcp_client_socket.send(msg.encode())
            else:
                print("Current GGA is not valid")
        print("\rreceive data: %.2f kB" % (receive_size / 1000.0), end=' ')

if __name__ == '__main__':
    request_for_RTCM3()
